#include "stdafx.h"
#include <windowsx.h>
#include "Application.h"
#include "Resource.h"

#include "Model.h"

Application * Application::sSingleInstance = NULL;

const int kREFRESHRATE = 60;

Application::Application(void) : mhWnd( NULL ) , mhInstance( NULL ) , mpModel( NULL ) , mpView( NULL )
{
	assert( Application::sSingleInstance == NULL );

	Application::sSingleInstance = this;
}

Application::~Application(void)
{
}

void Application::onIdle()
{
	DWORD tick = GetTickCount();
	DWORD diff = tick - this->muLastTick;
	if( diff < ( 1000 / kREFRESHRATE) )
		return;

	this->muLastTick = tick;
	// Basic Idle... should be overwritten
	// InvalidateRect( this->GetHWND() , NULL , false );
	this->getView()->Render();

}

HINSTANCE Application::GetINSTANCE() const
{
	return this->mhInstance;
}

HWND Application::GetHWND() const
{
	return this->mhWnd;
}

void Application::setModel( Model * inModel )
{
	this->mpModel = inModel;
}

void Application::setView( View * inView )
{
	this->mpView = inView;
}

int Application::Start( HINSTANCE hInstance , TCHAR * szTitle , TCHAR * szWindowClass , float fWidth , float fHeight , int nCmdShow )
{
	this->mfWidth	= fWidth;
	this->mfHeight	= fHeight;
	
	// Initialize global strings
	this->_myRegisterClass( hInstance , szWindowClass );

	// Perform application initialization:
	if (!this->_initInstance( hInstance , szTitle , szWindowClass , nCmdShow ) )
	{
		return FALSE;
	}

	// Initialize view and model.
	if( !this->onInit() )
	{
		return FALSE;
	}

	MSG msg;
	// HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FRAME));

	// Start Message Loop
	bool bQuit = false;
	while( 1 )
	{
		while( PeekMessage( &msg , NULL , 0 , 0 , PM_REMOVE ) )
		{
			if( msg.message == WM_QUIT )
			{
				bQuit = true;
				break;
			}

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if( bQuit ) break;

		// Idle processing.
		this->onIdle();
	}

	return (int) msg.wParam;
}

void Application::Destory()
{
	if( this->mpModel ) 
	{
		delete this->mpModel;
		this->mpModel = NULL;
	}

	if( this->mpView ) 
	{
		delete this->mpView;
		this->mpView = NULL;
	}
}


ATOM Application::_myRegisterClass( HINSTANCE hInstance , TCHAR * szWindowClass )
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FRAME));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;//MAKEINTRESOURCE(IDC_FRAME);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL Application::_initInstance( HINSTANCE hInstance , TCHAR * szTitle , TCHAR * szWindowClass , int nCmdShow )
{
   HWND hWnd;

   this->mhInstance = hInstance;

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, int( this->GetWidth() ) , int( this->GetHeight() ) , NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
   this->mhWnd = hWnd;

   ShowWindow(hWnd , nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

bool Application::MessageProc( UINT message, WPARAM wParam, LPARAM lParam )
{
	switch( message )
	{
	case WM_PAINT:
		{
			if( this->getView() )
			{
				this->getView()->Render();
			}
		}
		return true;
	case WM_LBUTTONDOWN:
		{
			if( this->getModel() ) this->getModel()->OnLButtonDown( float( GET_X_LPARAM( lParam ) ) , float( GET_Y_LPARAM( lParam ) ) );
		}
		return true;
	case WM_LBUTTONUP:
		{
			if( this->getModel() ) this->getModel()->OnLButtonUp( float( GET_X_LPARAM( lParam ) ) , float( GET_Y_LPARAM( lParam ) ) );
		}
		return true;
	case WM_MOUSEMOVE:
		{
			if( this->getModel() ) this->getModel()->OnMouseMove( float( GET_X_LPARAM( lParam ) ) , float( GET_Y_LPARAM( lParam ) ) );
		}
		return true;
	case WM_KEYDOWN:
		{
			if( this->getModel() ) this->getModel()->OnKeyDown( wParam );
		}
		return true;
	case WM_KEYUP:
		{
			if( this->getModel() ) this->getModel()->OnKeyUp( wParam );
		}
		return true;
	}
	return false;
}

LRESULT CALLBACK Application::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	Application *pApp = Application::sSingleInstance;

	if( pApp )
	{
		bool bProcessed = false;
		bProcessed = pApp->MessageProc( message, wParam, lParam );
		if( bProcessed )
		{
			return 0;
		}
	}			

	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
